#!/usr/bin/env bun

/**
 * n8n MCP 服务器
 *
 * 提供n8n工具集的MCP协议接口
 * 支持stdio和HTTP两种传输方式
 */

import { TOOLS_DIR } from '../config.js';
import { MCPProtocolHandler } from '../core/mcp-protocol-handler.js';
import { MCPStreamableHttpServer } from '../core/mcp-streamable-http.js';
import { globalToolRegistry } from '../core/tool-registry.js';
import type { DslSchema, Tool } from '../core/tool-schema.js';
import { createChildLogger, createErrorLogContext } from '../utils/logger.js';

/**
 * MCP服务器
 */
export class McpServer {
  handler: MCPProtocolHandler; // 公开 handler 以便外部访问
  private isInitialized = false;
  private toolsDir: string;
  private logger = createChildLogger('McpServer');

  constructor(toolsDir: string = TOOLS_DIR) {
    this.handler = new MCPProtocolHandler();
    this.toolsDir = toolsDir;
  }

  /**
   * 初始化服务器，动态发现并加载所有工具
   */
  async initialize(): Promise<void> {
    if (this.isInitialized) {
      return;
    }

    this.logger.info('Initializing MCP Server...');

    try {
      // 动态发现并加载工具包
      await globalToolRegistry.autoDiscoverTools(this.toolsDir);

      // 获取所有注册的工具并转换为MCP格式
      const toolDefinitions = globalToolRegistry.getAllTools();
      if (toolDefinitions.length === 0) {
        this.logger.warn('⚠️ WARNING: No tools were loaded!');
        throw new Error('No tools were loaded - check tools directory');
      }

      // 将每个工具定义转换并注册到MCP处理器
      for (const toolDef of toolDefinitions) {
        // 转换参数schema为DslSchema格式
        const dslSchema: DslSchema = {
          properties: toolDef.meta.parameters,
          required: Object.entries(toolDef.meta.parameters)
            .filter(([_, fieldSchema]) => fieldSchema.required)
            .map(([fieldName, _]) => fieldName),
          additionalProperties: false,
        };

        const mcpTool: Tool = {
          name: toolDef.meta.name,
          description: toolDef.meta.description || '',
          schema: dslSchema,
          execute: toolDef.handler,
        };
        this.handler.registerTool(mcpTool);
      }

      this.isInitialized = true;

      this.logger.info(`MCP Server initialized: ${toolDefinitions.length} tools ready`);
    } catch (error) {
      this.logger.error('Failed to initialize MCP Server', createErrorLogContext(error, {
        toolsDir: this.toolsDir
      }));
      throw error;
    }
  }

  /**
   * 启动stdio服务
   */
  async startStdio(): Promise<void> {
    await this.initialize();
    this.logger.info('Starting n8n MCP Server in stdio mode...');
    this.logger.info('✅ MCP Server ready for stdio communication');
    // 注意：以下调用会阻塞，持续监听stdio输入
    await this.handler.handleStdio(process.stdin, process.stdout);
  }

  /**
   * 启动Streamable HTTP服务
   */
  async startStreamableHttp(port: number = 3001, cors: boolean = true): Promise<void> {
    await this.initialize();
    this.logger.info(`Starting n8n MCP Server in Streamable HTTP mode on port ${port}...`);

    const streamableServer = new MCPStreamableHttpServer(this.handler, {
      port,
      cors,
      path: '/mcp',
    });

    await streamableServer.start();
  }

  /**
   * 启动普通HTTP服务
   */
  async startHttp(port: number = 3001): Promise<void> {
    await this.initialize();
    this.logger.info(`Starting n8n MCP Server in HTTP mode on port ${port}...`);

    const handler = this.handler; // 保存 handler 引用

    const server = Bun.serve({
      port,
      async fetch(request) {
        // CORS headers
        const headers = {
          'Content-Type': 'application/json',
          'Access-Control-Allow-Origin': '*',
          'Access-Control-Allow-Methods': 'POST, OPTIONS',
          'Access-Control-Allow-Headers': 'Content-Type',
        };

        // Handle preflight
        if (request.method === 'OPTIONS') {
          return new Response(null, { status: 204, headers });
        }

        // Handle MCP requests
        if (request.method === 'POST' && new URL(request.url).pathname === '/mcp') {
          try {
            const body = await request.text();
            const response = await handler.handleHttp(body);
            return new Response(response, { headers });
          } catch (error) {
            const logger = createChildLogger('McpServer-HTTP');
            logger.error('Failed to handle MCP HTTP request', createErrorLogContext(error, {
              url: request.url,
              method: request.method
            }));
            return new Response(
              JSON.stringify({
                jsonrpc: '2.0',
                error: {
                  code: -32700,
                  message: 'Parse error',
                },
              }),
              { status: 400, headers }
            );
          }
        }

        // Health check
        if (request.method === 'GET' && new URL(request.url).pathname === '/health') {
          return new Response(JSON.stringify({ status: 'ok', service: 'n8n-mcp-server' }), {
            headers,
          });
        }

        return new Response('Not Found', { status: 404 });
      },
    });

    this.logger.info(`n8n MCP Server running at http://localhost:${port}`);
    this.logger.info(`MCP endpoint: http://localhost:${port}/mcp`);
    this.logger.info(`Health check: http://localhost:${port}/health`);
  }

  /**
   * 获取处理器（用于测试）
   */
  getHandler(): MCPProtocolHandler {
    return this.handler;
  }
}

// 主入口
if (import.meta.main) {
  const server = new McpServer();

  // 解析命令行参数
  const args = process.argv.slice(2);
  const isHttp = args.includes('--http');
  const isStreamable = args.includes('--streamable') || args.includes('--mcp-http');
  const isCors = args.includes('--cors');
  const portIndex = args.findIndex(arg => arg === '--port');
  const portStr = portIndex >= 0 ? args[portIndex + 1] : undefined;
  const port = portStr ? parseInt(portStr) : 3001;

  // 启动服务
  if (isStreamable) {
    await server.startStreamableHttp(port, isCors);
  } else if (isHttp) {
    await server.startHttp(port);
  } else {
    await server.startStdio();
  }
}
